Android SQLite数据库升级解决方案

在使用SQLite数据库的时候,一般会有数据库版本的迭代。例如APP首次开发完成,数据库的版本是1.0版,然后需求增加,需要对数据库做改动,这时候数据库的版本就要更新为2.0。SQLite在遇见版本升级时会对比旧版本号和新版本号,执行onUpgrade()方法,需要自己在onUpgrade()方法中实现相应的逻辑。

SQLite版本升级的逻辑是:如果设备是首次安装这个APP,那么只会执行onCreate()方法;如果设备中安装有这个APP的旧版本,在安装新版本时,不会执行onCreate()方法,而只会执行onUpgrade()方法。

举个栗子

在数据库第一个版本中,我们新建一个people表,数据库版本号为1,SQLiteHelper类如下:

public class SQLiteHelper extends SQLiteOpenHelper {
    private static SQLiteHelper instance;
    private static final String DATABASE_NAME = "local_db";
      //初始版本号,固定不变
    private static final int FIRST_VERSION = 1;

    public static SQLiteHelper getInstance(Context context){
        if (instance == null){
            instance = new SQLiteHelper(context, DATABASE_NAME, null, NEW_VERSION);
        }

        return instance;
    }

    public SQLiteHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        //初始化时新建people表
        sqLiteDatabase.execSQL("create table if not exists people(" +
                "name text not null)");
    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {

    }
}

之后我们有了新的需求,需要再建一个job表,这时数据库的版本号升级为2,而且我们需要在onUpgrade()方法中加入相关代码。

public class SQLiteHelper extends SQLiteOpenHelper {
    private static SQLiteHelper instance;
    private static final String DATABASE_NAME = "local_db";
      //初始版本号,固定不变
    private static final int FIRST_VERSION = 1;
      //新的版本号
      private static final int NEW_VERSION = 2;

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        //初始化时新建people表
        sqLiteDatabase.execSQL("create table if not exists people(" +
                "name text not null)");
    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
        if (oldVersion == 1){
            sqLiteDatabase.execSQL("create table if not exists job(" +
                    "title text)");
        }
    }
}

onUpgrade()方法中,我们判断旧版本号如果为1的话就执行新的建表语句。这样可以解决从版本1升级到版本2的问题,但是如果用户直接从版本2开始安装呢?首次安装是不会执行onUpgrade()方法的,所以新的建表语句无法被执行到。对于这个问题,我们可以在onCreate()方法中手动调用onUpgrade()方法,oldVersion传入初始版本号,newVersion传入当前最新版本号:

@Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        //初始化时新建people表
        sqLiteDatabase.execSQL("create table if not exists people(" +
                "name text not null)");

        onUpgrade(sqLiteDatabase, FIRST_VERSION, NEW_VERSION);
    }

现在APP的数据库版本为2,这时我们又有了新的需求,需要新建一个company表。首先考虑从版本2升级到版本3的问题,那么在onUpgrade()方法中这样添加:

@Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
        if (oldVersion == 1){
            sqLiteDatabase.execSQL("create table if not exists job(" +
                    "title text)");
        }
        if (oldVersion == 2){
            sqLiteDatabase.execSQL("create table if not exists groups(" +
                    "name text not null)");
        }
    }

这样如果oldVersion为2,就会执行新的建表语句。但是如果oldVersion是1呢,从1直接升级到3,目前只会执行if(oldVersion == 1)中的语句。对于这个问题,有些解决方案是添加多个判断,把所有情况考虑完,比如1=>21=>32=>3,这3种情况下执行不同的操作,但是这样的话不仅麻烦,而且随着数据库版本的迭代,所要考虑的情况会越来越多。

对于这种情况,我的解决办法是使用递归的方式,从当前最老的版本开始,每次只考虑1个版本的间隔,一级一级的升级:

@Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
        if (oldVersion == 1){
            sqLiteDatabase.execSQL("create table if not exists job(" +
                    "title text)");
        }
        if (oldVersion == 2){
            sqLiteDatabase.execSQL("create table if not exists groups(" +
                    "name text not null)");
        }
        //递归调用
        if (newVersion-oldVersion > 1){
            onUpgrade(sqLiteDatabase, oldVersion+1, newVersion);
        }
    }

在递归的过程中,旧版本不断的+1,一直到和新版本只相差1,完成所有的升级操作。

完整的示例

public class SQLiteHelper extends SQLiteOpenHelper {
    private static SQLiteHelper instance;
    private static final String DATABASE_NAME = "local_db";
    private static final int FIRST_VERSION = 1;
    private static final int NEW_VERSION = 4;

    public static SQLiteHelper getInstance(Context context){
        if (instance == null){
            instance = new SQLiteHelper(context, DATABASE_NAME, null, NEW_VERSION);
        }

        return instance;
    }

    public static SQLiteDatabase getWritableDatabase(Context context){
        return getInstance(context).getWritableDatabase();
    }

    public static SQLiteDatabase getReadableDatabase(Context context){
        return getInstance(context).getReadableDatabase();
    }

    public SQLiteHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        sqLiteDatabase.execSQL("create table if not exists people(" +
                "name text not null)");

        onUpgrade(sqLiteDatabase, FIRST_VERSION, NEW_VERSION);
    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
        if (oldVersion == 1){
            sqLiteDatabase.execSQL("create table if not exists job(" +
                    "title text)");
        }
        if (oldVersion == 2){
            sqLiteDatabase.execSQL("create table if not exists groups(" +
                    "name text not null)");
        }
        if (oldVersion == 3){
            sqLiteDatabase.execSQL("create table if not exists company(" +
                    "name text not null)");
        }
        if (newVersion-oldVersion > 1){
            onUpgrade(sqLiteDatabase, oldVersion+1, newVersion);
        }
    }
}